home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK1.toast / Development Kits (Disc 1) / QuickTime / Programming Stuff / Documentation / develop articles / develop Issue 24 / Printing Compressed Images / JPEG Print / PrintJPEG.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-08-17  |  27.5 KB  |  1,122 lines  |  [TEXT/MPCC]

  1. /*
  2.  * PrintJPEG.c
  3.  
  4.     Authored by: David Gelphman 6/9/95. Borrowed liberally from the JFIF-PICT
  5.     conversion program provided as a sample program in the QuickTime development 
  6.     portion of the MacOS SDK.
  7.     
  8.     Copyright (c) Apple Computer Incorporated. All rights reserved.
  9. */
  10. #include "PrintJPEG.h"
  11.  
  12. /* globals  */
  13.  
  14. WindowPtr gTheWindow;
  15. JPEGImage gTheJPEGImage;
  16. THPrint    gPrinterRecord = NULL;
  17.  
  18. /* main */
  19. SInt16 main()
  20. {
  21. OSErr theErr;
  22.  
  23.     // initialize the managers and the menus
  24.     if (initMacintosh() != noErr)
  25.         return 0;
  26.     
  27.     /*  allocate a print record for us */
  28.     gPrinterRecord = (THPrint)NewHandle(sizeof( TPrint ) );
  29.  
  30.     // Get the defaults for the current printer driver.
  31.     if (gPrinterRecord != NULL && MemError() == noErr)
  32.     {
  33.         PrOpen(); 
  34.         PrintDefault( gPrinterRecord ); 
  35.         PrClose();
  36.     }else{
  37.         ParamText("\pCan't allocate Print Record.", "\p", "\p", "\p");
  38.         Alert(kGenericError, NULL);    
  39.         return iMemFullErr;    
  40.     }
  41.         
  42.     // Check to see if there are any errors and report them if they occur.
  43.     if(( theErr = PrError()) != noErr){
  44.         Str255 scratchString;
  45.         NumToString((long)theErr, scratchString);
  46.         ParamText("\pCan't Do PrDefault for some reason.", scratchString, "\p", "\p");
  47.         Alert(kGenericError, NULL);    
  48.     }
  49.     
  50.     // initialize our JPEGImage Data Structure.
  51.     gTheJPEGImage.theData = NULL;
  52.     SetRect(&gTheJPEGImage.theBounds,0,0,0,0);
  53.     gTheJPEGImage.theDesc = NULL;
  54.  
  55.     // Go ahead and get an image...
  56.     DoTheOpenCommand();
  57.     
  58.     // Process events, etc.
  59.     for (; handleEvent();)
  60.         {
  61.         }    /* for */
  62.     return 1;
  63. } /* main */
  64.     
  65.  
  66. //=====================================================================================
  67. // OSErr DrawJPEGImage(JPEGImage theJPEGImage, Boolean isColorPort)
  68. //=====================================================================================
  69. // Draws a JPEG image, whose data is in theJPEGImage.theData, to the current port.
  70. //=====================================================================================
  71.  
  72.  
  73. static OSErr DrawJPEGImage(JPEGImage theJPEGImage, Boolean isColorPort)
  74. {
  75.     Rect srcRect;
  76.  
  77.     OSErr theErr = noErr;
  78.     Handle theDataHandle = theJPEGImage.theData;
  79.     Ptr        theDataPtr ;
  80.     ImageDescriptionHandle theDesc = theJPEGImage.theDesc;
  81.     
  82.     if (theDataHandle != NULL){
  83.         PixMapHandle dPixMap = ((CGrafPtr)qd.thePort)->portPixMap;
  84.  
  85.         char hState = HGetState(theDataHandle);
  86.         HLock(theDataHandle);                // Lock down the data before using *theDataHandle
  87.         theDataPtr = StripAddress(*theDataHandle);
  88.             
  89.         // srcRect will be the source rectangle of the image we will draw 
  90.         SetRect(&srcRect, 0, 0, (*theDesc)->width, (*theDesc)->height);            
  91.                                 
  92.         if(isColorPort){                    
  93.             MatrixRecord theMatrix;
  94.             CQDProcs myStdProcs;
  95.             StdPixProcPtr MyProcPtr;
  96.             // set both flag bits described in IM QuickTime p.3-139 for StdPix
  97.             short flags = kCallOldBits | kCallStdBits;        
  98.  
  99.             // we need a pixmap for our call to the StdPix bottleneck
  100.             PixMapHandle SpecialPixMapH = NewPixMap();
  101.             PixMapPtr SpecialPixMapP;
  102.     
  103.             if(SpecialPixMapH != NULL && MemError() == noErr){
  104.  
  105.                 MoveHHi((Handle)SpecialPixMapH);
  106.                 HLock((Handle)SpecialPixMapH);
  107.             
  108.                 SpecialPixMapP = (PixMapPtr)StripAddress(*(Handle)SpecialPixMapH);
  109.                 
  110.                 // make a matrix which describes the mapping between the source
  111.                 // and destination rectangles. In our case, they will be the
  112.                 // same rectangle
  113.  
  114.                 RectMatrix(&theMatrix, &srcRect, &srcRect);
  115.                 
  116.                 // SetCompressedPixMapInfo makes a compressed 'PixMap' structure into SpecialPixMapP, using 
  117.                 // the ImageDescription in 'theDesc' with the compressed image data in 'theDataPtr'
  118.     
  119.                 theErr = SetCompressedPixMapInfo(SpecialPixMapP, theDesc, theDataPtr, 0,NULL,NULL);
  120.                 
  121.                 if(theErr == noErr){
  122.                     // Look to see if there are custom QuickDraw bottlenecks in the current port.
  123.                     if( (((CGrafPtr)qd.thePort)->grafProcs) == NULL)
  124.                     {
  125.                         // Get the Standard Bottleneck procs. 
  126.                         SetStdCProcs(&myStdProcs);
  127.                         
  128.                         // The 'newProc1' bottleneck is the 'StdPix' bottleneck.
  129.                         MyProcPtr = (StdPixProcPtr)myStdProcs.newProc1;
  130.                         
  131.                     }else{
  132.                         // Use the custom 'StdPix' bottleneck in the grafPort.
  133.                         // The 'newProc1' bottleneck is the 'StdPix' bottleneck.
  134.  
  135.                         MyProcPtr = (StdPixProcPtr)((CGrafPtr)qd.thePort)->grafProcs->newProc1;
  136.                     }
  137.                     
  138.                     // Call the bottleneck. The function call being used here is:
  139.                     // StdPix(SpecialPixMapP, &srcRect, &theMatrix, ditherCopy, NULL, NULL,NULL,flags)
  140.                     // with the appropriate StdPix bottleneck.
  141.                      
  142.                     CallStdPixProc(MyProcPtr,SpecialPixMapP, &srcRect, &theMatrix, 
  143.                         ditherCopy, NULL, NULL,NULL,flags );
  144.                 }
  145.                 HUnlock((Handle)SpecialPixMapH);
  146.             }
  147.             if(SpecialPixMapH)DisposePixMap(SpecialPixMapH);
  148.         }else{
  149.             // Come here for a B&W port since there is no StdPix bottleneck for a B&W grafport. 
  150.             // By definition there isn't a custom StdPix bottleneck in a B&W grafport. 
  151.             // We'll let QuickTime figure out how to handle this case. 
  152.             CodecComponent theCodec;
  153.             
  154.             if ( theErr == noErr) {
  155.                 CodecInfo theInfo;
  156.                 
  157.                 // use the bestFidelityCodec if it is available, otherwise just use any
  158.                 theErr = GetCodecInfo(&theInfo, 'jpeg', bestFidelityCodec); 
  159.             if(theErr == noErr){
  160.                 theCodec = bestFidelityCodec;
  161.             }else
  162.                 // we know this Codec exists because at startup we checked in the InitMacintosh proc
  163.                 theCodec = anyCodec;    
  164.             }
  165.  
  166.             theErr = FDecompressImage(theDataPtr, theDesc, 
  167.                             dPixMap, &srcRect, NULL, ditherCopy,
  168.                             NULL, NULL, NULL, codecMaxQuality, theCodec, 0, NULL, NULL);            
  169.         }
  170.         HSetState(theDataHandle, hState);
  171.         return theErr;
  172.     }
  173. }    /* DrawJPEGImage */
  174.  
  175.  
  176. static void DoTheOpenCommand()
  177. {
  178. OSErr theErr = noErr;
  179. OSType theTypes[] = { 'JPEG' };
  180. StandardFileReply theReply;
  181. Rect bounds;
  182. SInt16 windowTop = GetMBarHeight()+ 19;
  183. SInt16 right,bottom;
  184.  
  185.     // check to see if we already have data. If so, we'll dispose of it since we are going to open a new image.
  186.     if( gTheJPEGImage.theData != NULL){
  187.         DisposeHandle(gTheJPEGImage.theData);
  188.         gTheJPEGImage.theData = NULL;
  189.     }
  190.  
  191.     // disable the Print menu item since we can't print data anymore.
  192.     DisableItem(GetMHandle(kFileMenuID), kPrintCommand);
  193.  
  194.     // Hide the existing window, if there is one.
  195.     if(gTheWindow != NULL)HideWindow(gTheWindow);
  196.  
  197.     // Get a JPEG file and read the data.
  198.     if(theErr == noErr){
  199.         StandardGetFile(NULL, 1, theTypes, &theReply);
  200.         if (theReply.sfGood) {
  201.             theErr = ReadJPEGData(&(theReply.sfFile), &(gTheJPEGImage.theData));
  202.             if(theErr != noErr){
  203.                 ParamText("\pNot Enough Memory To Handle this Image", "\p", "\p", "\p");
  204.                 Alert(kGenericError, NULL);    
  205.             }
  206.         }else {
  207.             // no file selected so just return.
  208.             return;
  209.         };
  210.     };
  211.  
  212.     // Make a JPEG Image Description for the current image.
  213.     if (theErr == noErr) theErr = MakeJPEGImageDescription(&gTheJPEGImage);
  214.     
  215.     if(theErr == iNotJPEGData){
  216.         ParamText(
  217.         "\pThe selected file either doesn't contain JPEG data or a JPEG version that can't be read by this program."
  218.         , "\p", "\p", "\p");
  219.         Alert(kGenericError, NULL);    
  220.  
  221.     }
  222.     
  223.     // Set the bounds data in the JPEGData structure.
  224.     if (theErr == noErr) theErr = SetJPEGBounds(&gTheJPEGImage);
  225.     
  226.     
  227.  
  228.     // Now make a Window of an appropriate size to hold our image. If we already
  229.     // have a window then size it properly.
  230.  
  231.  
  232.     // Max out the Window size at approximately the main screen size.    
  233.     if (theErr == noErr) {
  234.         right = MIN(gTheJPEGImage.theBounds.right , qd.screenBits.bounds.right - kInsetBits );
  235.         bottom = MIN(gTheJPEGImage.theBounds.bottom, qd.screenBits.bounds.bottom - (kInsetBits+windowTop) );
  236.         SetRect(&bounds, 0 ,0 , right, bottom);
  237.     }
  238.     
  239.     if (theErr == noErr){
  240.         
  241.         OffsetRect(&bounds, 0, windowTop);
  242.  
  243.         if(gTheWindow == NULL) {
  244.             gTheWindow = NewCWindow(NULL, &bounds,    
  245.                     (StringPtr)&(theReply.sfFile.name),
  246.                     false, noGrowDocProc, (WindowPtr)-1, true, 0);
  247.             if (gTheWindow == NULL) {
  248.                 theErr = iMemFullErr;    
  249.             }
  250.         } else{
  251.             MoveWindow (gTheWindow, 0, windowTop, true);    
  252.             SetWTitle(gTheWindow, theReply.sfFile.name);
  253.             SizeWindow (gTheWindow, bounds.right - bounds.left, bounds.bottom-bounds.top, true);
  254.         }
  255.     }
  256.     
  257.     if (theErr == noErr) {
  258.         AlignWindow(gTheWindow, true, NULL, NULL);
  259.         ShowWindow(gTheWindow);
  260.         SetPort(gTheWindow);
  261.         OffsetRect(&bounds, 0, -windowTop);
  262.         ClipRect(&bounds);
  263.     };
  264.  
  265.     // Since we have an image, we'll enable the 'Print' menu item.
  266.     
  267.     if (theErr == noErr) {
  268.       EnableItem(GetMHandle(kFileMenuID), kPrintCommand);
  269.     }        
  270.     
  271.     // Invalidate the Window contents so we'll get an update event.
  272.     if( theErr == noErr)InvalRect(&gTheWindow->portRect);
  273.  
  274.     DrawMenuBar();
  275.     
  276.     return;
  277. } /* DoTheOpenCommand */
  278.  
  279. // Initialize the World and check whether the appropriate managers are available.
  280. static OSErr initMacintosh()
  281. {
  282.     long version;
  283.     OSErr theErr = noErr;
  284.        
  285.     InitGraf(&qd.thePort);
  286.     InitFonts();
  287.     InitWindows();
  288.     FlushEvents(everyEvent, 0);
  289.       InitDialogs(NULL);
  290.     TEInit();
  291.     InitCursor();
  292.     SetFractEnable(true);            // I guess this urge comes from my old days at Adobe
  293.     
  294.     theErr = SetupMenus();
  295.     
  296.     if(theErr != noErr){
  297.         ParamText("\pCouldn't load Menus.", "\p", "\p", "\p");
  298.     }
  299.  
  300.     if(theErr == noErr){
  301.         theErr = Gestalt(gestaltSystemVersion, &version);
  302.         if( (theErr != noErr) || (version < 0x0700) ){
  303.                 ParamText("\pThis software requires System 7.0 or higher.", "\p", "\p", "\p");
  304.                 theErr = kAppError;
  305.         }
  306.     }    
  307.  
  308.     if(theErr == noErr){
  309.         theErr = Gestalt(gestaltQuickTimeVersion, &version);
  310.         if( (theErr != noErr) ){
  311.                 ParamText("\pQuickTime not installed.  This demo requires Quicktime.", "\p", "\p", "\p");
  312.         }
  313.     }    
  314.     
  315.     if(theErr == noErr){
  316.         theErr = Gestalt(gestaltCompressionMgr, &version);
  317. //    Checking for version 1.5 of the ICM comes from the JFIF-PICT conversion program 
  318. //    provided as a sample program in the QuickTime section of the SDK. Apparently there
  319. //    are problems with versions prior to version 1.5.    
  320.         if( (theErr != noErr || version < 15) ){
  321.                 if(theErr == noErr) theErr = kAppError;
  322.                 ParamText("\pVersion 1.5 or Greater of the Image Compression Mgr is required.", "\p", "\p", "\p");
  323.         }
  324.     }    
  325.  
  326.     if(theErr == noErr){
  327.         theErr = Gestalt(gestaltComponentMgr, &version);
  328.         if( (theErr != noErr) ){
  329.                 ParamText("\pThe component manager is required but it isn't available.", "\p", "\p", "\p");
  330.         }
  331.     }
  332.     
  333.     if ( theErr == noErr) {        // make sure there is some Codec available that can handle JPEG.
  334.         CodecInfo theInfo;
  335.         
  336.         theErr = GetCodecInfo(&theInfo, 'jpeg', anyCodec);
  337.         if(theErr != noErr)ParamText("\pThere don't appear to be any JPEG decompressors!", "\p", "\p", "\p");        
  338.     }
  339.     
  340.     if(theErr != noErr){
  341.         Alert(kGenericError, NULL);    
  342.     }
  343.     
  344.     return theErr;
  345. }    /* initMacintosh */
  346.  
  347.  
  348. static SInt16 handleEvent()
  349. {
  350.     EventRecord e;
  351.     WindowRecord *w;
  352.     SInt32 s;
  353.     Rect b;
  354.     RgnHandle currentGrayRegion;
  355.     OSErr theErr;
  356.     GrafPtr oldPort;
  357.     
  358.     if (WaitNextEvent(everyEvent, &e, kSleepTime, 0L))
  359.         {
  360.         switch (e.what)
  361.             {
  362.             case mouseDown:
  363.                 switch (FindWindow(e.where, (WindowPtr *)&w))
  364.                     {
  365.                     case inContent:
  366.                         if ((WindowRecord *) FrontWindow() != w)
  367.                             SelectWindow((WindowPtr)w);
  368.                         else if (((WindowRecord *) w)->windowKind < 0)
  369.                             SystemClick(&e, (WindowPtr )w);
  370.                         break;
  371.                     case inDrag:
  372.                         currentGrayRegion = GetGrayRgn();
  373.                         b = (**(RgnHandle)currentGrayRegion).rgnBBox;
  374.                         // make sure drags align well for best performance in redraws.
  375.                         DragAlignedWindow((WindowPtr )w, e.where, &b, NULL, NULL);
  376.                         break;
  377.                     case inGoAway:
  378.                         if (TrackGoAway((WindowPtr )w, e.where)){
  379.                             HideWindow((WindowPtr )w);
  380.                             DisableItem(GetMHandle(kFileMenuID), kPrintCommand);
  381.                             DrawMenuBar();
  382.                         }
  383.                         break;
  384.                     case inGrow:
  385.                         SetRect(&b, 30, 30, 0x7fff, 0x7fff);
  386.                         s = GrowWindow((WindowPtr )w, e.where, &b);
  387.                         if (s){
  388.                             SizeWindow((WindowPtr )w, LoWord(s), HiWord(s), 1);
  389.                             SetPort((WindowPtr )w);
  390.                             InvalRect(&gTheWindow->portRect);
  391.                         }
  392.                         break;
  393.                     case inZoomIn:
  394.                         if (TrackBox((WindowPtr )w, e.where, inZoomIn)){
  395.                             ZoomWindow((WindowPtr )w, inZoomIn, 0);
  396.                             SetPort((WindowPtr )w);
  397.                             InvalRect(&gTheWindow->portRect);
  398.                         }
  399.                         break;
  400.                     case inZoomOut:
  401.                         if (TrackBox((WindowPtr )w, e.where, inZoomOut)){
  402.                             ZoomWindow((WindowPtr )w, inZoomOut, 0);
  403.                             SetPort((WindowPtr )w);
  404.                             InvalRect(&gTheWindow->portRect);
  405.                         }
  406.                         break;
  407.                     case inMenuBar: 
  408.                     {
  409.                         SInt32 mResult = MenuSelect(e.where);
  410.                           SInt16 theMenu, theItem;
  411.                           theMenu = HiWord(mResult); 
  412.                           theItem = LoWord(mResult);
  413.                         doCommand(theMenu, theItem);
  414.                           break;
  415.                       }
  416.                 }
  417.                 break;
  418.             case updateEvt:
  419.                 BeginUpdate((WindowPtr)e.message);
  420.                 GetPort(&oldPort);
  421.                 SetPort((WindowPtr)e.message);
  422.                 
  423.                 theErr = DrawJPEGImage(gTheJPEGImage,ISCOLORPORT((WindowPtr)e.message));
  424.  
  425.                 SetPort(oldPort);
  426.                 EndUpdate((WindowPtr)e.message);
  427.  
  428.                 break;
  429.             case keyDown:
  430.               case autoKey:
  431.                 DoKeyDown(&e);
  432.                 break;
  433.               default: break;
  434.  
  435.             }    /* switch */
  436.         }    /* if */
  437.     return 1;
  438. }    /* handleEvent */
  439.  
  440.  
  441. static OSErr SetupMenus(void) 
  442. {
  443.     OSErr theErr = noErr;
  444.     Handle MyMBar;
  445.         
  446.     MyMBar = GetNewMBar(128);
  447.     if(MyMBar) {
  448.         SetMenuBar(MyMBar);
  449.         AddResMenu(GetMHandle(kAppleMenuID),'DRVR');
  450.         DisableItem(GetMHandle(kFileMenuID), kPrintCommand);
  451.         DrawMenuBar();
  452.     } else {
  453.         theErr = kCouldntLoadMenu;
  454.     }
  455.     return theErr;
  456. } /* SetupMenus */
  457.  
  458. static void DoKeyDown(myEvent) 
  459.     EventRecord *myEvent; 
  460. {
  461.     unsigned char charCode = (unsigned char) (myEvent->message & charCodeMask);
  462.     
  463.     if (myEvent->modifiers & cmdKey) 
  464.       { /* command key */
  465.         SInt32 mResult = MenuKey(charCode);
  466.         SInt16 theMenu = HiWord(mResult); 
  467.         SInt16 theItem = LoWord(mResult);
  468.         doCommand(theMenu, theItem);
  469.     }
  470.     return ;
  471. }    /* KeyDown */
  472.  
  473.  
  474. /*
  475.    Display the About dialog.
  476. */
  477. static void showAboutMeDialog() 
  478. {
  479.   GrafPtr savePort;
  480.   SInt16 itemHit;
  481.   DialogPtr theDialog;
  482.  
  483.   GetPort(&savePort);
  484.   theDialog = GetNewDialog(kAboutMeDLOG, NULL, (WindowPtr) -1);
  485.   SetPort(theDialog);
  486.  
  487.   do { ModalDialog(NULL, &itemHit); } while (itemHit != kOKButton);
  488.  
  489.   CloseDialog(theDialog);
  490.  
  491.   SetPort(savePort);
  492.   return;
  493. }    /* showAboutMeDialog */
  494.  
  495. /*
  496.  * Process Menu Selections
  497. */
  498. static void doCommand(SInt16 theMenu,SInt16 theItem)
  499. {
  500. OSErr theErr = noErr ;
  501.  
  502.       switch (theMenu) 
  503.       {
  504.         case kAppleMenuID:
  505.           if (theItem == kAboutMeCommand) 
  506.           {
  507.                 showAboutMeDialog();
  508.           }
  509.           else 
  510.           {
  511.             GrafPtr savePort;
  512.             Str255 daName;
  513.             GetItem(GetMHandle(kAppleMenuID), theItem, daName);
  514.             GetPort(&savePort);
  515.             (void) OpenDeskAcc(daName);
  516.             SetPort(savePort);
  517.           }
  518.           break;
  519.     
  520.         case kFileMenuID:
  521.           switch (theItem) 
  522.           {
  523.             case kOpenCommand:
  524.                 DoTheOpenCommand();
  525.                 break;
  526.             case kSaveCommand:
  527.                 break;
  528.             case kPageSetupCommand:
  529.                                 
  530.                 PrOpen();
  531.                 theErr = PrError();
  532.                     
  533.                 if(theErr == noErr ){
  534.                     PrValidate( gPrinterRecord );
  535.                     theErr = PrError();    
  536.                 }
  537.  
  538.                 if(theErr == noErr){
  539.                     PrStlDialog(gPrinterRecord);
  540.                     theErr = PrError();
  541.                 }
  542.                 PrClose();
  543.                 
  544.                 if(theErr != noErr){
  545.                     Str255 scratchString;
  546.                     NumToString((long)theErr, scratchString);
  547.                     ParamText("\pThere was a print manager error.", scratchString, "\p", "\p");
  548.                     Alert(kGenericError, NULL);    
  549.                 }
  550.                 break;
  551.             case kPrintCommand:
  552.                 theErr = Print(gTheJPEGImage, gPrinterRecord);
  553.                 
  554.                 if(theErr != noErr){
  555.                     Str255 scratchString;
  556.                     NumToString((long)theErr, scratchString);
  557.                     ParamText("\pThere was an error during printing.", scratchString, "\p", "\p");
  558.                     Alert(kGenericError, NULL);    
  559.                 }
  560.                 break;
  561.             case kQuitCommand:
  562.                 ExitToShell();
  563.                 break;
  564.             default:
  565.                 break;
  566.           }
  567.           break;
  568.     
  569.         case kEditMenuID:
  570.           /*
  571.            * Run this through SystemEdit first.
  572.            * SystemEdit will return FALSE if it's not a system window.
  573.            */
  574.           if (SystemEdit(theItem-1)) break;
  575.         
  576.         default:
  577.           break;
  578.         } /* switch theMenu */
  579.     
  580.       HiliteMenu(0);
  581.       return;
  582. }    /* doCommand */
  583.  
  584.  
  585. static OSErr Print(JPEGImage theJPEGImage,  THPrint  thePrintRecord)
  586. {
  587. OSErr theDrawingErr = noErr;
  588. OSErr aPrintingErr = noErr;
  589.  
  590. GrafPtr oldPort;
  591. TPrStatus thePrinterStatus;
  592. TPPrPort PrinterPort;
  593. Boolean OKToPrint;
  594.  
  595.     GetPort( &oldPort ); 
  596.             
  597.     PrOpen();
  598.         
  599.     if( PrError() == noErr){
  600.         PrValidate(thePrintRecord);
  601.         if( PrError() == noErr){
  602.             OKToPrint = PrJobDialog(thePrintRecord);
  603.             if(OKToPrint){
  604.                 PrinterPort = PrOpenDoc(thePrintRecord,NULL,NULL);
  605.                 if(PrError() == noErr){
  606.                     PrOpenPage(PrinterPort,NULL);
  607.                     if(PrError() == noErr){
  608.                         theDrawingErr = DrawJPEGImage(theJPEGImage,ISCOLORPORT((GrafPtr)PrinterPort));
  609.                     }
  610.                     PrClosePage(PrinterPort);
  611.                 }
  612.                 PrCloseDoc(PrinterPort);
  613.                 if( 
  614.                     ( (*thePrintRecord)->prJob.bJDocLoop == bSpoolLoop ) && 
  615.                     ( PrError() == noErr )
  616.                 ){
  617.                     PrPicFile(thePrintRecord,NULL,NULL,NULL, &thePrinterStatus);
  618.                 }
  619.             }
  620.         } 
  621.     }
  622.     aPrintingErr = PrError();
  623.     PrClose();
  624.     SetPort(oldPort);
  625.     return ( (aPrintingErr == noErr) ? theDrawingErr : aPrintingErr) ;
  626. }    /* Print */
  627.  
  628.  
  629.  
  630. // Read the JPEG data from the file into a handle
  631. static OSErr ReadJPEGData(FSSpec *theSpec, Handle *theData)
  632. {
  633.     OSErr theErr;
  634.     short refNum;
  635.     long size;
  636.     
  637.     theErr = FSpOpenDF(theSpec, fsRdPerm, &refNum);
  638.     if (theErr == noErr) {
  639.         theErr = GetEOF(refNum, &size);
  640.         if (theErr == noErr) {
  641.             if ((*theData = NewHandle(size)) && (MemError() == noErr) ) {
  642.                 MoveHHi(*theData);
  643.                 HLock(*theData);
  644.                 theErr = FSRead(refNum, &size, **theData);
  645.                 HUnlock(*theData);
  646.             } else theErr = memFullErr;
  647.         }
  648.         FSClose(refNum);
  649.     }
  650.     return theErr;
  651. } /* ReadJPEGData */
  652.  
  653.  
  654.  
  655. // SetJPEGBounds sets 'theBounds' field of the JPEG data structure. This relies on the
  656. // ImageDescription field having already been filled in.
  657. static  OSErr SetJPEGBounds(JPEGImage *theJPEGImage)
  658. {
  659.     Rect *theBounds = &(*theJPEGImage).theBounds;
  660.     ImageDescriptionHandle theDesc = (*theJPEGImage).theDesc;
  661.     
  662.     if(theDesc == NULL)return kAppError;
  663.     
  664.     theBounds->top = theBounds->left = 0;
  665.     
  666.     theBounds->right =  (*theDesc)->width;
  667.     theBounds->bottom = (*theDesc)->height;
  668.     return noErr;
  669. }    /* SetJPEGBounds */
  670.  
  671. // MakeJPEGImageDescription creates a QuickTime image description record based on the JPEG data.
  672. static  OSErr MakeJPEGImageDescription(JPEGImage *theJPEGImage) 
  673. {
  674.     OSErr theErr = noErr;
  675.     CodecInfo theInfo;
  676.     short width,height;
  677.     ImageDescriptionHandle desc;
  678.     char    *scanData;
  679.     long    hRes = 72L<<16 , vRes = 72L<<16;
  680.     short    depth = 32;
  681.     Ptr theDataPtr;
  682.     
  683.     if((*theJPEGImage).theDesc == NULL){
  684.         (*theJPEGImage).theDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
  685.         if((*theJPEGImage).theDesc == NULL || (MemError() != noErr)){
  686.             theErr = iMemFullErr;
  687.             return theErr;
  688.         }
  689.     }
  690.     
  691.     HLock((*theJPEGImage).theData);
  692.     theDataPtr = StripAddress(*(theJPEGImage->theData));
  693.         
  694.     // Scan the JPEG image data and find the width, height, resolution, and depth of the image
  695.     if ( (scanData = MarkerDetect((char *)theDataPtr,&width,&height,&hRes,&vRes,&depth)) == 0 ) {
  696.         theErr = iNotJPEGData;
  697.     }
  698.  
  699.     // This chooses the Codec that can handle JPEG data with the best Fidelity. If that returns an
  700.     // error we'll try and use the best we can find that can handle JPEG. 
  701.     // This will only be used if we are decompressing the data on the host instead
  702.     // of the printer.
  703.     if ( theErr == noErr) {
  704.         theErr = GetCodecInfo(&theInfo, 'jpeg', bestFidelityCodec);
  705.         if(theErr != noErr)
  706.             theErr = GetCodecInfo(&theInfo, 'jpeg', anyCodec);    // we know this exists because we check at 
  707.                                                                 // at App startup in the InitMacintosh proc
  708.     }
  709.     // Set up the ImageDescription based on the JPEG Codec and the JPEG image data.
  710.     if ( theErr == noErr){
  711.         desc =     (*theJPEGImage).theDesc;
  712.         
  713.         (*desc)->idSize = sizeof(ImageDescription);
  714.         (*desc)->cType = 'jpeg';
  715.         (*desc)->resvd1 = (*desc)->resvd2 = (*desc)->dataRefIndex = 0;
  716.         (*desc)->version = theInfo.version;
  717.         (*desc)->revisionLevel = theInfo.revisionLevel;
  718.         (*desc)->vendor = theInfo.vendor;
  719.         (*desc)->temporalQuality = 0;
  720.         (*desc)->spatialQuality = codecMaxQuality;
  721.         (*desc)->width = width;
  722.         (*desc)->height = height;
  723.         (*desc)->hRes = hRes;
  724.         (*desc)->vRes = vRes;
  725.         (*desc)->dataSize = GetHandleSize((*theJPEGImage).theData);
  726.         (*desc)->frameCount = 1;
  727.         BlockMove(theInfo.typeName, (*desc)->name, 32);
  728.         (*desc)->depth = depth;
  729.         (*desc)->clutID = -1;
  730.     }
  731.     HUnlock((*theJPEGImage).theData);
  732.     return theErr;
  733. } /* MakeJPEGImageDescription */
  734.  
  735.  
  736. /*
  737.  
  738.     JPEG specific stuff. Taken almost literally from the Macintosh OS SDK.
  739.     This is from the JFIF-PICT conversion program provided as a sample program 
  740.     in the QuickTime section of the SDK. The only change was to allow
  741.     JFIF version 1.02 to be used with this application.
  742.     
  743. */
  744.     
  745. /*
  746.  
  747.     Scan the JPEG stream for the proper markers and fill in the image parameters
  748.     
  749.     returns NULL if it cant comprehend the data, otherwise a pointer to the start
  750.     of the JPEG data.
  751.     
  752.     
  753.     It does a cursory check on the JPEG data to see if it's reasonable.
  754.     Check out the ISO JPEG spec if you really want to know what's going on here.
  755.     
  756. */
  757.  
  758. static char *
  759. MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth)
  760. {
  761.     short    frame_field_length;
  762.     short    data_precision;
  763.     short    scan_field_length;
  764.     short    number_component,scan_components;
  765.     short    c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
  766.     short    dac_t1, dac_t2, dac_t3;
  767.     unsigned char    c;
  768.     short    qtabledefn;
  769.     short    htabledefn;
  770.     short    status;
  771.     short    length;
  772.     short    i;
  773.     
  774.     c = *data++;
  775.     qtabledefn = 0;
  776.     htabledefn = 0;
  777.     status = 0;
  778.     while (c != (unsigned char)MARKER_SOS) {
  779.         while (c != (unsigned char)MARKER_PREFIX)
  780.             c = *data++;                        /* looking for marker prefix bytes */
  781.         while (c == (unsigned char)MARKER_PREFIX)
  782.             c = *data++;                        /* (multiple?) marker prefix bytes */
  783.         if (c == 0)
  784.             continue;                                    /* 0 is never a marker code */
  785.  
  786.         if (c == (unsigned char)MARKER_SOF) {
  787.  
  788.             frame_field_length = *(short *)data;
  789.             data += 2;
  790.             data_precision = *data++;
  791.             
  792.             if ( data_precision != 8 ) { 
  793.                 status = 2;
  794.             }
  795.  
  796.             *height = *(short *)data;
  797.             data += 2;
  798.             *width = *(short *)data;
  799.             data += 2;
  800.                         
  801.             number_component = *data++;
  802.             
  803.             switch ( number_component  ) {
  804.             case 3:
  805.                 c1 = *data++;
  806.                 hv1 = *data++;
  807.                 q1 = *data++;
  808.                 c2 = *data++;
  809.                 hv2 = *data++;
  810.                 q2 = *data++;
  811.                 c3 = *data++;
  812.                 hv3 = *data++;
  813.                 q3 = *data++;
  814.                 *depth = 32;
  815.                 break;
  816.             case 1:        
  817.                 c1 = *data++;
  818.                 hv1 = *data++;
  819.                 q1 = *data++;
  820.                 *depth = 40;
  821.                 break;
  822.             default:
  823.                 status = 3;
  824.                 break;
  825.             }
  826.             continue;
  827.         }
  828.     
  829.         if (c == (unsigned char)MARKER_SOS) {
  830.             short tn;
  831.             scan_field_length = *(short *)data;
  832.             data += 2;
  833.             scan_components = *data++;
  834.             for ( i=0; i < scan_components; i++ ) {
  835.                 unsigned char cn,dac_t;
  836.                 
  837.                 cn = *data++;
  838.                 dac_t = *data++;
  839.                 if ( cn == c1 ) {
  840.                     dac_t1 = dac_t;
  841.                 } else if ( cn == c2 ) {
  842.                     dac_t2 = dac_t;
  843.                 } else if ( cn == c3 ) {
  844.                     dac_t3 = dac_t;
  845.                 } else {    
  846.                     status = 29;
  847.                     break;
  848.                 }
  849.             }
  850.             switch ( tn=(dac_t1 & 0xf) )  {
  851.             case 0:
  852.             case 1:
  853.                 break;
  854.             case 0xf:
  855.                 break;
  856.             default:
  857.                 status = 33;
  858.                 break;
  859.             }
  860.             switch (  tn=(dac_t2 & 0xf) )  {
  861.             case 0:
  862.             case 1:
  863.                 break;
  864.             case 0xf:
  865.                 break;
  866.             default:
  867.                 status = 33;
  868.                 break;
  869.             }
  870.             switch (  tn=(dac_t3 & 0xf) )  {
  871.             case 0:
  872.             case 1:
  873.                 break;
  874.             case 0xf:
  875.                 break;
  876.             default:
  877.                 status = 33;
  878.                 break;
  879.             }
  880.  
  881.  
  882.             /*  Initialize the DC tables */
  883.             
  884.             switch (  tn=dac_t1 & 0xf0 )  {
  885.             case 0:
  886.             case 0x10:
  887.                 break;
  888.             case 0xf0:
  889.                 break;
  890.             default:
  891.                 status = 34;
  892.                 break;
  893.             }
  894.             switch (  tn=dac_t2 & 0xf0 )  {
  895.             case 0:
  896.             case 0x10:
  897.                 break;
  898.             case 0xf0:
  899.                 break;
  900.             default:
  901.                 status = 34;
  902.                 break;
  903.             }
  904.             switch (  tn=dac_t3 & 0xf0 )  {
  905.             case 0:
  906.             case 0x10:
  907.                 break;
  908.             case 0xf0:
  909.                 break;
  910.             default:
  911.                 status = 34;
  912.                 break;
  913.             }
  914.             if ( *data++ != 0 )  {
  915. //                status = 18;
  916.             }
  917.             if ( *data++ != 63 )  {
  918. //                status = 19;
  919.             }
  920.             if ( *data++ != 0 ) {
  921. //                status = 20;
  922.             }
  923.             if ( status )
  924.                 return(0);
  925.             else
  926.                 return(data);
  927.         }
  928.  
  929.         if (c == (unsigned char)MARKER_DQT) {
  930.             scan_field_length = *(short *)data;
  931.             SwallowQuantTable(data);
  932.             data += scan_field_length;
  933.             continue;
  934.         }
  935.         if (c == (unsigned char)MARKER_DHT) {
  936.             scan_field_length = *(short *)data;
  937.             SwallowHuffTable(data);
  938.             continue;
  939.         }
  940.         if (c == (unsigned char)MARKER_DRI) {
  941.             length = *(short *)data;            /* read length */
  942.             data += 2;
  943.             length = *(short *)data;            
  944.             data += 2;
  945.             continue;
  946.         }
  947.         if (c == (unsigned char)MARKER_DNL) {
  948.             length = *(short *)data;            /* read length */
  949.             data += 2;
  950.             length = *(short *)data;            
  951.             data += 2;
  952.             continue;
  953.         }
  954.         if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
  955.             continue;
  956.         }
  957.  
  958.         if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI)    /* image start, end marker */
  959.             continue;
  960.  
  961.         if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
  962.             status = 12;
  963.             length = *(short *)data;            /* read length */
  964.             data += length;
  965.             continue;
  966.         }
  967.         if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
  968.             length = *(short *)data;            /* read length */
  969.             data += 2;
  970.             length -= 2;
  971.             if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
  972.                 char buf[5];
  973.                 buf[0] = *data++;
  974.                 buf[1] = *data++;
  975.                 buf[2] = *data++;
  976.                 buf[3] = *data++;
  977.                 buf[4] = *data++;
  978.                 length -= 5;
  979.                 
  980.                 if ( buf[0] == 'J' && buf[1] == 'F'  && buf[2] == 'I'  && buf[3] == 'F' ) {
  981.                     short    units;
  982.                     long    xres,yres;
  983.                     short    version;
  984.                     
  985.                     
  986.                     version = *(short *)data; data += 2;length -= 2;
  987.                     if ( version != 0x100 && version != 0x101 && version != 0x102 ) { // DMG - added version 102
  988.                         status = 44;        // unknown JFIF version
  989.                         break;
  990.                     }
  991.                     units = *data++; length--;
  992.                     xres = *(short *)data; data += 2; length -= 2;
  993.                     yres = *(short *)data; data += 2; length -= 2;
  994.  
  995.                     switch ( units ) {
  996.                     case 0:            // no res, just aspect ratio
  997.                         *hRes = FixMul(72L<<16,xres<<16);
  998.                         *vRes = FixMul(72L<<16,yres<<16);
  999.                         break;
  1000.                     case 1:            // dots per inch
  1001.                         *hRes = xres<<16;
  1002.                         *vRes = yres<<16;
  1003.                         break;
  1004.                     case 2:            // dots per centimeter (we convert to dpi )
  1005.                         *hRes = FixMul(0x28a3d,xres<<16);
  1006.                         *vRes = FixMul(0x28a3d,xres<<16);
  1007.                         break;    
  1008.                     default:
  1009.                         break;
  1010.                     }
  1011.                     xres = *data++; length--;
  1012.                     yres = *data++; length--;
  1013.                     
  1014.                     /* skip JFIF thumbnail */
  1015.                     
  1016.                     xres *= yres;
  1017.                     data += xres*3; length -= xres*3;
  1018.                     
  1019.                     if (  length != 0 ) {
  1020.                         status = 44;        // bad jfif marker
  1021.                         break;
  1022.                     }
  1023.                 }
  1024.             }
  1025.             data += length;
  1026.             continue;
  1027.         }
  1028.         if (c == (unsigned char)MARKER_COM) {
  1029.             length = *(short *)data;            /* read length */
  1030.             data += length;
  1031.             continue;
  1032.         }
  1033.         if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
  1034.             length = *(short *)data;            /* read length */
  1035.             data += length;
  1036.             continue;
  1037.         }
  1038.         if ( c == 0x1 )
  1039.             continue;
  1040.         if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
  1041.             length = *(short *)data;            /* read length */
  1042.             status = 13;
  1043.             data += length;
  1044.             continue;
  1045.         }
  1046.     }
  1047.     return(0);
  1048. }
  1049.  
  1050.  
  1051. /*
  1052.     Read the quantization table from the JPEG bitstream.
  1053. */
  1054.  
  1055. static void 
  1056. SwallowQuantTable(char *data)
  1057. {
  1058.     long    i;
  1059.     long    length,pm,nm;
  1060.  
  1061.     length = *(short *)data;            /* read length */
  1062.     length -= 2;
  1063.     data += 2;
  1064.     while ( length ) {
  1065.         nm= *data++;                    /* read precision and number */
  1066.         pm = nm>>4;    
  1067.         nm &= 0xf;
  1068.         length--;
  1069.         if ( pm ) {
  1070.             for(i=0;i<64;i++) {
  1071.                 length -= 2;
  1072.                 data += 2;
  1073.             }
  1074.         } else {
  1075.             for(i=0;i<64;i++) {
  1076.                 length--;
  1077.                 data++;
  1078.             }
  1079.         }
  1080.     }    
  1081. }
  1082.  
  1083. /*
  1084.     Read the huffman table from the JPEG bitstream.
  1085. */
  1086.  
  1087. static void 
  1088. SwallowHuffTable(char *data)
  1089. {
  1090.     short    i,tc,id;
  1091.     long    length;
  1092.     
  1093.     unsigned char    bin[17];
  1094.     unsigned char    val[256];
  1095.  
  1096.     bin[0] = 0;
  1097.     length = *(short *)data;            /* read length */
  1098.     data += 2;
  1099.     length -= 2;
  1100.     while ( length ) {
  1101.         id=*data++;                /* read id */
  1102.         length--;
  1103.         if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
  1104.             return;
  1105.         }
  1106.         tc = 0;
  1107.         for(i=0;i<16;i++) {
  1108.             length--;
  1109.             tc += (bin[i+1] = *data++);
  1110.         }
  1111.         for (i=0; i < tc; i++ ) {
  1112.             length--;
  1113.             val[i] = *data++;
  1114.         }
  1115.     }
  1116. }
  1117.     
  1118.  
  1119.  
  1120.  
  1121.  
  1122.